package com.lagou.work02.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.work02.stereotype.MyAutowired;
import com.lagou.work02.stereotype.MyService;
import com.lagou.work02.stereotype.MyTransactional;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.Resource;

import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;
import java.io.InputStream;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class BeanFactory {

    private static Map<String,Object> map = new HashMap<>();  // 存储对象

    static {
        try {
            /**
             * 解析配置文件获取扫描路径 com.lagou.work02
             */
            String aPackage = getPackage("bean.xml");

            /**
             * 扫描指定包路径，并将包含@MyService注解的类存放在map中
             * 注解中设置了value值以设定值为map的key值，未设置值以首字母小写的类名为key
             */
            getMyServiceMap(aPackage);

            /**
             * 迭代存放BeanMap集合，查找出包含@MyAutowired注解的类的方法
             * 并通过set方法进行装配
             */
            getMyAutowiredMap();

            /**
             * 迭代存放BeanMap集合，查找出包含@MyTransactional注解的类
             * 并判断该类是否实现接口，若实现则采用cglib代理，否则采用jdk代理
             */
            getProxyMap();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 迭代存放BeanMap集合，查找出包含@MyTransactional注解的类
     * 并判断该类是否实现接口，若实现则采用cglib代理，否则采用jdk代理
     */
    private static void getProxyMap() {
        for (String id : map.keySet()){
            Object object = map.get(id);
            Class aClass = object.getClass();
            if (!aClass.isAnnotationPresent(MyTransactional.class)) {
                continue;
            }
            ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
            Type[] genericInterfaces = aClass.getGenericInterfaces();
            if (genericInterfaces == null || genericInterfaces.length == 0) { // jdk代理
                object = proxyFactory.getJDKProxy(object);
            } else { // cglib代理
                object = proxyFactory.getCglibProxy(object);
            }
            // 把处理之后的parentObject重新放到map中
            map.put(id,object);
        }
    }

    /**
     * 迭代存放BeanMap集合，查找出包含@MyAutowired注解的类的方法
     * 并通过set方法进行装配
     * @throws Exception
     */
    private static void getMyAutowiredMap() throws Exception {
        for (String parentId : map.keySet()){
            Object parentObject = map.get(parentId);
            Class aClass = parentObject.getClass();
            Method[] methods = aClass.getMethods();

            for (Method method : methods) {
                if (!method.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                }
                String methodName = method.getName();
                String autowiredKey = methodName.substring(3,methodName.length());
                autowiredKey = lowerFirst(autowiredKey);
                Object autowiredObject = map.get(autowiredKey);

                method.invoke(parentObject,autowiredObject);
            }
            // 把处理之后的parentObject重新放到map中
            map.put(parentId,parentObject);
        }
    }

    /**
     * 扫描指定包路径，并将包含@MyService注解的类存放在map中
     * 注解中设置了value值以设定值为map的key值，未设置值以首字母小写的类名为key
     * @param aPackage
     * @throws Exception
     */
    private static void getMyServiceMap(String aPackage) throws Exception {
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(null);
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
        Resource[] resources = resolver.getResources(aPackage);
        for (Resource r : resources) {
            MetadataReader reader = metaReader.getMetadataReader(r);
            String className = reader.getClassMetadata().getClassName();
            Class<?> aClass = Class.forName(className);

            if(!aClass.isAnnotationPresent(MyService.class)) {
                continue;
            }
            //是否添加自定义注解
            MyService annotation = aClass.getAnnotation(MyService.class);
            String id = annotation.value();
            if (StringUtils.isEmpty(id)) {
                id = className.substring(className.lastIndexOf(".") + 1, className.length());
                id = lowerFirst(id); // 首字母小写
            }
            Object o = aClass.newInstance();
            map.put(id, o);
        }
    }

    // 实现首字母小写
    private static String lowerFirst(String name){
        char[] chars = name.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 解析配置文件获取扫描路径 com.lagou.work02
     * @param path
     * @return
     * @throws DocumentException
     */
    private static String getPackage(String path) throws DocumentException {
        InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream(path);
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();

        Element scanElement = (Element) rootElement.selectNodes("//scan").get(0);

        String aPackage = scanElement.attributeValue("package");
        aPackage = aPackage.replace(".","/");
        aPackage = "classpath*:" + aPackage + "/**/*.class";
        return aPackage;
    }

    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }

}
